#ifndef SDUNIVERSE_H
#define SDUNIVERSE_H

#include <vector>
#include <set>
#include <math.h>
#include <map>
#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include "../src/chipmunk.h"
#include "sdInput.h"
#include "sdObject.h"
#include "sdPlayer.h"
#include "sdBad.h"
#include "sdBadChunk.h"

//Holds and implements all the objects in the game
class sdUniverse {
private:
	int height_; //Width and height of the universe
	int width_;
	sdInput input_; //Input class
    cpSpace* space_; //Chipmunks space hash, holds cpBody's
    sdPlayer player_; //The human player
    static bool killPlayer_; //If the human player needs to die next step
    bool quitting_; //Has quitting initiated? (has the player died)
    cpFloat timeToQuit_; //Countdown to quit after the player dies
    
    cpFloat nextSpawnTime_; //Time until next enemy spawns
    cpFloat timeToSpawn_; //How much longer left until enemy spawns
    cpFloat deltaSpawn_; //Change in spawn times over time
	cpFloat badVel; //How fast the enemies are at the moment
    cpFloat badTurn; //How fast the enemies turn at the moment
    static int kills; //How many kills you have
    
    cpBody* statics_; //A body for the immovable objects (walls)
    
    static sdUniverse* this_; //Pointer to this to allow for callbacks
    
    static std::map<cpShape*, sdObject*> objects_; //cpShape -> sdObject map to allow drawing
    static std::set<sdObject*> doomed; //contains objects that need to die at the end of the next step
    static std::set<sdTimed*> timed_; //objects that we need to tick
    static std::set<sdBad*> bad_; //Bad guys
    
	//Collision callbacks
    static int sdCollisionBW(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
    static int sdCollisionSW(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
    static int sdCollisionBS(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
    static int sdCollisionBE(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
    static int sdCollisionSE(cpShape *a, cpShape *b, cpContact *contacts, int numContacts, cpFloat normal_coef, void *data);
    
    static sdUniverse* getUniverse() {return this_;}
public:
	//Constructor
    sdUniverse(int height, int width)
        : height_(height), width_(width), input_(height, width), space_(cpSpaceNew()), player_() {
        this_ = this;
        init();
        
		//Set the difficulty options
        nextSpawnTime_ = 2.0;
        timeToSpawn_ = nextSpawnTime_;
        deltaSpawn_ = 0.05;
        badVel = 5000.0;
        badTurn = 1.0;
		
		//Set the control variables
        killPlayer_ = false;
        quitting_ = false;
        timeToQuit_ = 2.0;

		kills = 0;
    }

    cpSpace* getSpace() {return space_;}

    void init(); //Init the universe
    void update(); //Update the universe
    void updateObjects(cpFloat ticks); //
    void moveBads();

	static std::map<cpShape*, sdObject*>& getObjects() {
		return objects_;
	}
    
    void updateThis(sdTimed& timed) {timed_.insert(&timed);}
    
    void doom(sdObject& o);
    void kill();
    void insert(sdObject& o);
    
    void addBad(int hp, int hpMax, cpFloat velocity, cpFloat turn, cpVect pos);
    cpVect randomLoc() {
        cpVect result;
                
        do {
            result = cpv((rand() % height_) - height_ / 2, (rand() % width_) - width_ / 2);
        } while(abs(result.x - player_.getShip().getPos().x) < 200 && abs(result.y - player_.getShip().getPos().y) < 200);
        
        return result;
    }
    
    /*int collision(cpShape *a,
                  cpShape *b,
                  cpContact *contacts,
                  int numContacts,
                  cpFloat normal_coef,
                  void *data) {
        //std::cout << "Colliding Key[" << a << "] Key[" << b << "]";
        //std::cout << " Value[" << objects_[a] << "] Value[" << objects_[b] << "]" << std::endl;
        objects_[a]->collide(NULL);
        objects_[b]->collide(NULL);
        //printf("colliding objects: %p  vs  %p\n", objects[a], objects[b]);
        return 1;
    }*/
    
    //players
    inline sdPlayer& getPlayer() {return player_;}    
	
	//input
	inline sdInput& getInputClass() {return input_;}
	
	/*static int collisionCb(cpShape *a,
	                        cpShape *b,
	                        cpContact *contacts,
	                        int numContacts,
	                        cpFloat normal_coef,
	                        void *data) {
	    return sdUniverse::getUniverse()->collision(a, b, contacts, numContacts, normal_coef, data);
	}*/
};

#endif
